package com.softcomputo.decodificador;

import java.io.IOException;
import java.util.Date;

/**
 * @author Luis Miguel Hernández Pérez
 */
@SuppressWarnings("unused")
public class DicomReaderH {
    /* Variables de la informacion del archivo */

    public byte[] data;
    private int index = 0;
    /* Este es el la Presentacion del Valor */
    private static final String VR_ = "1.2.840.10008.1.2";
    public static final int MAX_HEADER_SIZE = 10000;
    private int maxHeaderSize;
    public static final String ImplicitVRLittleEndian = VR_;
    public static final int _ImplicitVRLittleEndian = 0;
    public static final String ExplicitVRLittleEndian = VR_ + ".1";
    public static final int _ExplicitVRLittleEndian = 1;
    public static final String ExplicitVRBigEndian = VR_ + ".2";
    public static final int _ExplicitVRBigEndian = 2;
    public static final int _ImplicitVRBigEndian = 3;
    public static final String JPEGCompression = VR_ + ".4.";
    public static final int _JPEGCompression = 10;
    public static final String RLECompression = VR_ + ".5";
    public static final int _RLECompression = 20;
    public static final int _notUnderstood = 1000;
	private boolean oneSamplePerPixel = true;
    private boolean oneFramePerFile = true;
    /* Datos contenidos en el archivo de imagen diagnostica */
    private String VRString;
    private String xSyntaxUID = "";//	0x0002,0x0010
    private String imageType = "";//	0x0008,0x0008
    private String fecha_realizacion = "";//	0x0008,0x0030	 
    private String modality = "";// 0x0008,0x0060
    private String manufacturer = "";//	0x0008,0x0070
    private String institution = "";//	0x0008,0x0080
    private String physician = "";//	0x0008,0x0090
    private String nombre_paciente = "";//	0x0010,0x0010
    private String id_paciente = "";//	0x0010,0x0020
    private String fecha_nacimiento_paciente = "";//	0x0010,0x0030
    private String sexo = "";//	0x0010,0x0040
    private int numberOfFrames = 1;	// 0x0028,0x0008 //IS : integer string
    private int samplesPerPixel = 1;	// 0x0028,0x0002
    private int heigth = -1; 	// 0x0028,0x0010
    private int width = -1;	// 0x0028, 0x0011
    private int bitsAllocated = -1;	// 0x0028, 0x0100
    private int bitsAmacenamineto = -1;	// 0x0028, 0x0101
    private int highBit = -1; 	// 0x0028, 0x0102
    private int indicado = -1;	// 0x0028, 0x0103(pixelRepresentation )
    private int size = -1;	// 0x7Fe0, 0x0010
    private int n = -1;	// = 1 or 2 
    private int VR = -1;	// value representation 
    private boolean bE = false;
    private int errorDetector = -1;

    public DicomReaderH(byte[] dicom) {
        data = dicom;
        index = 0;
        cargamosInformacionDelArchivo();
    }

    private void cargamosInformacionDelArchivo() {
        calcularTamanioMaximo();
        obtenerPresentacionValor();
        cargarInformacionArchivoDicom();
    }

    private void calcularTamanioMaximo() {
        maxHeaderSize = MAX_HEADER_SIZE * 1;
        if (maxHeaderSize > data.length) {
            maxHeaderSize = data.length;
        }
    }

    private void obtenerPresentacionValor() {
        xSyntaxUID = getaString(0x0002, 0x0010, index);
//        //System.Out.Println("\n-------------Ê\nTransfertsyntaxUID			" + xSyntaxUID);
        if (!xSyntaxUID.trim().isEmpty()) {
            xSyntaxUID = xSyntaxUID.trim();
            if (xSyntaxUID.equals(ImplicitVRLittleEndian)) {
                VR = _ImplicitVRLittleEndian;
            } else if (xSyntaxUID.equals(ExplicitVRLittleEndian)) {
                VR = _ExplicitVRLittleEndian;
            } else if (xSyntaxUID.equals(ExplicitVRBigEndian)) {
                VR = _ExplicitVRBigEndian;
            } else if (xSyntaxUID.startsWith(JPEGCompression)) {
                VR = _JPEGCompression;
            } else if (xSyntaxUID.startsWith(RLECompression)) {
                VR = _RLECompression;
            } else {
                VR = _notUnderstood;
            }
            switch (VR) {
                case _ImplicitVRLittleEndian:
                    VRString = "ImplicitVRLittleEndian	";
                    break;
                case _ExplicitVRLittleEndian:
                    VRString = "ExplicitVRLittleEndian	";
                    break;
                case _ExplicitVRBigEndian:
                    VRString = "ExplicitVRBigEndian	";
                    break;
                case _JPEGCompression:
                    VRString = "JPEGCompression	";
                    break;
                case _RLECompression:
                    VRString = "RLECompression	";
                    break;
                case _notUnderstood:
                    VRString = "not understood	";
                    break;
                default:
                    VRString = " Something curious happened !";
            }// end of switch
            //System.Out.Println("Value representation		: " + VRString);
        }//end if !Unknown	
        //	Second, if we have failed to find the VR from the file; let's try to figure it !
        if (VR == _notUnderstood) {
            if ((data[0] != 'D') || (data[1] != 'I') || (data[2] != 'C') || (data[3] != 'M')) {
                if (data[0] < data[1]) {
                    VR = _ImplicitVRBigEndian;
                    bE = true;
                }//endif(dat..
            }
        }// end if VR==_notUnderstood	
    }

    /**
     * Cargamos informacion del archivo 
     */
    private void cargarInformacionArchivoDicom() {
        imageType = getaString(0x0008, 0x0008, index);
        fecha_realizacion = getaString(0x0008, 0x0020, index);
        modality = getaString(0x0008, 0x0060, index);
        manufacturer = getaString(0x0008, 0x0070, index);
        institution = getaString(0x0008, 0x0080, index);
        physician = getaString(0x0008, 0x0090, index);
        nombre_paciente = getaString(0x0010, 0x0010, index);
        id_paciente = getaString(0x0010, 0x0020, index);
        fecha_nacimiento_paciente = getaString(0x0010, 0x0030, index);
        sexo = getaString(0x0010, 0x0040, index);

        heigth = getAnInt(0x0028, 0x0010, index);// heigth
        if (heigth == -1) {// give a secod chance : by beginning parsing from 0.
            index = 0;
            heigth = getAnInt(0x0028, 0x0010);
//            //System.Out.Println("Something is going wrong in this file  !!!, h == -1");
        }
        width = getAnInt(0x0028, 0x0011, index); // width
        if (width == -1) {
            index = 0;
            width = getAnInt(0x0028, 0x0011);
//            //System.Out.Println("Nouvelle valeur de  w avec  0x0028, 0x0011  : " + width);
//            //System.Out.Println("Something is gooing wrong in this file  !!!, w == -1");
        }

        bitsAllocated = getAnInt(0x0028, 0x0100, index);
//        //System.Out.Println("bitsAllocated ......." + bitsAllocated);
        if (bitsAllocated == -1) {
            index = 0;
            bitsAllocated = getAnInt(0x0028, 0x0100);
//            //System.Out.Println("Nouvelle valeur de  bitsAllocated: " + bitsAllocated);
        }
        bitsAmacenamineto = getAnInt(0x0028, 0x0101, index);
//        //System.Out.Println("bitsStored ......." + bitsAmacenamineto);
        if (bitsAmacenamineto == -1) {
            index = 0;
            bitsAmacenamineto = getAnInt(0x0028, 0x0101);
//            //System.Out.Println("Nouvelle valeur de  bitsStored : " + bitsAmacenamineto);
        }
        highBit = getAnInt(0x0028, 0x0102, index);
        //System.Out.Println("highBit ......." + highBit);
        if (highBit == -1) {
            index = 0;
            highBit = getAnInt(0x0028, 0x0102);
            //System.Out.Println("Nouvelle valeur de  highBit  : " + highBit);
        }
        indicado = getAnInt(0x0028, 0x0103, index);//(pixelRepresentation )
        //System.Out.Println("signed ......." + indicado);
        if (indicado == -1) {
            index = 0;
            indicado = getAnInt(0x0028, 0x0103);
            //System.Out.Println("Nouvelle valeur de  signed  : " + indicado);
        }

//        //System.Out.Println(" ImageType ::: " + imageType);
//        //System.Out.Println(" studyDate ::: " + fecha_realizacion);
//        //System.Out.Println(" modality ::: " + modality);
//        //System.Out.Println(" manufacturer ::: 	" + manufacturer);
//        //System.Out.Println(" Institution ::: " + institution);
//        //System.Out.Println(" physician  ::: " + physician);
//        //System.Out.Println(" patientName :::	" + nombre_paciente);
//        //System.Out.Println(" patientID ::: " + id_paciente);
//        //System.Out.Println(" patientBirthdate ::: " + fecha_nacimiento_paciente);
//        //System.Out.Println(" sex ::: " + sexo);
//        //System.Out.Println(" samplesPerPixel ::: " + samplesPerPixel);
//        //System.Out.Println(" h ::: " + heigth);
//        //System.Out.Println(" w ::: " + width);
//        //System.Out.Println(" bitsAllocated ::: " + bitsAllocated);
//        //System.Out.Println(" bitsStored ::: 	" + bitsAmacenamineto);
//        //System.Out.Println(" highBit ::: 				" + highBit);
//        //System.Out.Println(" signed ::: 				" + indicado);


        /**
         * First we figure a tSize or theoritical size, then we compare it to 
         * the information contained in the Dicom file. 
         * True only if there is no image compression.
         */
        // Look for the Pixel Length 's tag position :
        int pos = buscarPosicionInformacion(0x7Fe0, 0x0010, index);
        if (pos != -1) {
            size = getTamanioInformacion(pos + 4);
        }
        //System.Out.Println(" size1 :::		 		" + size);
        int HeaderSize = pos;
        //System.Out.Println("\nHeaderSize    : " + HeaderSize);


        int tSize = samplesPerPixel * width * heigth * bitsAllocated / 8;// theoritical Size	
        int figuredSize = HeaderSize + 8 + tSize;
        errorDetector = data.length - figuredSize;
        //System.Out.Println("Data Length -  Theoriticaly_figuredSize  : " + errorDetector);

        if (errorDetector == 4) {
            size = getTamanioInformacion(pos + 8); // try explicit VRStringize
            errorDetector = data.length - size;
        }

        if (errorDetector != 0) { //see what's wrong :

            //1 more than one sample per pixel :	
            samplesPerPixel = getAnInt(0x0028, 0x0002);// not mandatory ?
            if (samplesPerPixel < 0 || samplesPerPixel > 3) {
                samplesPerPixel = 1;// default value
                //System.Out.Println(" NEW samplesPerPixel ::: 	" + samplesPerPixel);
            } else if (samplesPerPixel == 1) {
                oneSamplePerPixel = true;
            } else {
                oneSamplePerPixel = false;
            }


            //2 more than one Frame :	
            try {
                numberOfFrames = Integer.parseInt(getaString(0x0028, 0x0008));
            } catch (NumberFormatException nFE) {
                numberOfFrames = 1;
            }
            if (numberOfFrames > 1) {
                oneFramePerFile = false;
            }
            tSize = numberOfFrames * tSize * samplesPerPixel;
            figuredSize = HeaderSize + 8 + tSize;
            errorDetector = data.length - figuredSize;

            //3 other cases :
            if (VR == _JPEGCompression) {
                //System.Out.Println("_JPEGCompression , can't read that file");
            }

            if (VR == _RLECompression) {
                //System.Out.Println(" RLECompression , can't read that file");
            }

            //System.Out.Println("2 Byte difference , error detector: " + errorDetector
                    //+ "\n Frame per file  " + numberOfFrames
                    //+ "\n samplesPerPixel " + samplesPerPixel);
        }
    }

    /***/
    public String getaString(int groupElement, int dataElement) {
        return getaString(groupElement, dataElement, 0);
    }

    private String getaString(int groupElement, int dataElement, int j) {
        int pos = buscarPosicionInformacion(groupElement, dataElement, j);
        if (pos < MAX_HEADER_SIZE && pos != -1) {
            int length = getTamanioInformacion(pos + 4);
            if (length > 256) {
                length = leerInt16(pos + 6);
            }
            if (length > 64 || length < 0) {
                length = 64;
            }
            if (length > (data.length - pos - 8)) {
                length = data.length - pos - 9;
            }
            index = pos;
            pos += 8;
            char[] patName = new char[length];
            for (int i = 0; i < length; i++) {
                patName[i] = (char) data[pos++];
            }
            return new String(patName);
        } else {
            return " Unknown ";
        }
    }
    
    private int buscarPosicionInformacion(int groupElement, int dataElement, int j) {
        int LenMax = data.length - 3; // -3 because we don't want to have an arrayOutOfBounds exception 
        boolean found = false;
        byte testByte1 = (byte) (groupElement & 0xff);
        byte testByte2 = (byte) ((groupElement & 0xff00) >> 8);
        byte testByte3 = (byte) (dataElement & 0xff);
        byte testByte4 = (byte) ((dataElement & 0xff00) >> 8);

        //debug(" Looking for :"+ Tools.toHex(testByte1)+Tools.toHex(testByte2)+Tools.toHex(testByte3)+ Tools.toHex(testByte4));

        for (; j < LenMax || found; j++) {
            if ((data[j] == testByte1) && (data[j + 1] == testByte2) && (data[j + 2] == testByte3) && (data[j + 3] == testByte4)) {
                found = true;
                return j;
            }//endif
        }
        return -1;
    }//endOfmethod

    private int getTamanioInformacion(int i) {
        int i0 = (int) (data[i] & 0xff);
        int i1 = (int) (data[i + 1] & 0xff);
        int i2 = (int) (data[i + 2] & 0xff);
        int i3 = (int) (data[i + 3] & 0xff);
        return i3 << 24 | i2 << 16 | i1 << 8 | i0;
    }

    private int leerInt32(int i) {
        return getTamanioInformacion(i);//same method !
    }

    private int leerInt16(int i) {
        int i1 = data[i + 1] & 0xff;
        int i0 = data[i] & 0xff;
        int anInt = i1 << 8 | i0;
        if (anInt < -1) {
            anInt = (int) (data[i] * 256) & 0xff + data[i + 1] & 0xff;//swap byt	
        }
        return anInt;
    }

    public int getAnInt(int groupElement, int dataElement) {
        return getAnInt(groupElement, dataElement, 0);
    }//endofMethod

    private int getAnInt(int groupElement, int dataElement, int j) {
        int pos = buscarPosicionInformacion(groupElement, dataElement, j);
        if (pos < maxHeaderSize && pos != -1) {
            index = pos;
            //debug( "message length :" + readMessageLength(pos+4) );
            if (getTamanioInformacion(pos + 4) == 2) {
                return leerInt16(pos + 8);
            } else if (getTamanioInformacion(pos + 4) == 4) {
                return leerInt32(pos + 8);
            } else { // case we meet an explicit VR case .
                int h = leerInt16(pos + 8);
                if (h < 6000) {
                    return h;// we must prevent here something weird.
                } else {
                    return -1;
                }
            }
        } else {
            return -1;
        }
    }
    
    public int getPixelRepresentacion(){return	indicado;}
    
    
    /* getter and setter */

    public byte[] getData() throws Exception{
       
        if (VR == _JPEGCompression) {
            throw new IOException("DICOM JPEG no soportada");
        }

        if (width == -1) {
            throw new IOException("Formato no sincronizado");
        }
        if (heigth == -1) {
            throw new IOException("Formato no reconocido");
        }

        int ba = bitsAllocated;
        if (ba % 8 == 0) {
            ba = ba / 8;
        } else {
            ba = (ba + 8) / 8;
        }
        int fileLength = width * heigth * ba;
        int offset = data.length - fileLength;	

        byte[] pixData = new byte[fileLength];
        java.lang.System.arraycopy(data, offset, pixData, 0, fileLength);
        return pixData;
    }
    
    
    

    public void setData(byte[] data) {
        this.data = data;
    }

    public int getHeigth() {
        return heigth;
    }

    public void setHeigth(int heigth) {
        this.heigth = heigth;
    }

    public String getSexo() {
        return sexo;
    }

    public void setSexo(String sex) {
        this.sexo = sex;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getN() {
        return n;
    }

    public void setN(int n) {
        this.n = n;
    }

    public int getBitsAllocated() {
        return bitsAllocated;
    }

    public void setBitsAllocated(int bitsAllocated) {
        this.bitsAllocated = bitsAllocated;
    }

    public int getBitsAmacenamineto() {
        return bitsAmacenamineto;
    }

    public void setBitsAmacenamineto(int bitsAmacenamineto) {
        this.bitsAmacenamineto = bitsAmacenamineto;
    }

    public int getHighBit() {
        return highBit;
    }

    public void setHighBit(int highBit) {
        this.highBit = highBit;
    }

    public String getId_paciente() {
        return id_paciente;
    }

    public void setId_paciente(String id_paciente) {
        this.id_paciente = id_paciente;
    }

    public String getNombre_paciente() {
        return nombre_paciente.replace("^", " ");
    }

    public void setNombre_paciente(String nombre_paciente) {
        this.nombre_paciente = nombre_paciente;
    }

	public Date getFecha_realizacion() {
		return  new Date(Long.parseLong(fecha_realizacion));  
	}
}
